package api

import (
	"encoding/json"
	"fmt"
	"time"

	"net/http"
	"strconv"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/gorilla/mux"
	"github.com/rafttest/util"
)

var peers = []raft.Peer{{1, nil}}
var nt = newRaftNetwork(1, 2)
var nodes = make([]*node, 0)

func addPeer(peer raft.Peer) bool {
	//判断是否存在
	for _, p := range peers {
		if peer.ID == p.ID {
			return false
		}
	}

	fmt.Printf("%v\n", peers)
	peers = append(peers, peer)
	fmt.Printf("%v\n", peers)
	return true
}

func removePeer(peerId uint64) bool {
	index := -1
	for i, p := range peers {
		if p.ID == peerId {
			index = i
			break
		}
	}

	if index == -1 {
		return false
	}

	fmt.Printf("%v\n", peers)
	peers = append(peers[:index], peers[index+1:]...)
	fmt.Printf("%v\n", peers)

	return true
}

func hasPeer(peerId uint64) bool {
	for _, p := range peers {
		if p.ID == peerId {
			return true
		}
	}

	return false
}

func getNode(peerId uint64) *node {
	for _, n := range nodes {
		if n.id == uint64(peerId) {
			return n
		}
	}

	return nil
}

func startNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("startNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	if !hasPeer(uint64(node_id_int)) {
		fmt.Printf("the peer not exist")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	ok := getNode(uint64(node_id_int))
	if ok != nil {
		fmt.Printf("node_id already exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := startNode(uint64(node_id_int), peers, nt.nodeNetwork(uint64(node_id_int)))
	n.Node.Campaign(context.TODO())
	nodes = append(nodes, n)

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func stopNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("stopNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := getNode(uint64(node_id_int))
	if n == nil {
		fmt.Printf("get Node failure, node not exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n.stop()
	if n.state.Commit != 10006 {
		fmt.Printf("[error]commit = %d, want = 10006\n", n.state.Commit)
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func restartNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("restartNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := getNode(uint64(node_id_int))
	if n == nil {
		fmt.Printf("get Node failure, node not exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n.restart()

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func pauseNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("restartNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := getNode(uint64(node_id_int))
	if n == nil {
		fmt.Printf("get Node failure, node not exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n.pause()

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func resumeNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("restartNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	if len(nodes) < node_id_int {
		fmt.Printf("node_id out of bound\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := getNode(uint64(node_id_int))
	if n == nil {
		fmt.Printf("get Node failure, node not exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n.resume()

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func addNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("addNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	//添加peer
	if flag := addPeer(raft.Peer{uint64(node_id_int), nil}); !flag {
		fmt.Printf("add peer failure\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	//启动节点
	node := startNode(uint64(node_id_int), peers, nt.nodeNetwork(uint64(node_id_int)))
	nodes = append(nodes, node)
	node.Node.Campaign(context.TODO())

	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeAddNode,
		NodeID:  uint64(node_id_int),
		Context: []byte(node_id),
	}

	//遍历所有节点
	for _, n := range nodes {
		if n.id == uint64(node_id_int) {
			continue
		}

		err = n.ProposeConfChange(context.TODO(), cc)
		if err != nil {
			fmt.Printf("node ProposeConfChange failure, err:%v\n", err)
			util.Response(nil, http.StatusBadRequest, resp)
			return
		}
		break
		//st := n.ApplyConfChange(cc)
		//fmt.Printf("state: %v\n", st.String())
	}

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func removeNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]

	fmt.Printf("removeNode Request, node_id:%v\n", node_id)

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	//删除peer
	if flag := removePeer(uint64(node_id_int)); !flag {
		fmt.Printf("remove peer failure\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeRemoveNode,
		NodeID:  uint64(node_id_int),
		Context: []byte(node_id),
	}

	//遍历所有节点
	for _, n := range nodes {
		err = n.ProposeConfChange(context.TODO(), cc)
		if err != nil {
			fmt.Printf("node ProposeConfChange failure, err:%v\n", err)
			util.Response(nil, http.StatusBadRequest, resp)
			return
		}
		break
		//st := n.ApplyConfChange(cc)
		//fmt.Printf("state: %v\n", st.String())
	}

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func proposeNodeHandler(resp http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	node_id := vars["node_id"]
	data := vars["data"]

	fmt.Printf("proposeNode Request, node_id:%v data:%v\n", node_id, data)

	m := make(map[string]string)
	m["data"] = data
	m["time"] = fmt.Sprintf("%v", time.Now())

	json_data, err := json.Marshal(m)
	if err != nil {
		fmt.Printf("data json Marshal error\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	node_id_int, err := strconv.Atoi(node_id)
	if err != nil {
		fmt.Printf("字符串转换成整数失败")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	n := getNode(uint64(node_id_int))
	if n == nil {
		fmt.Printf("get Node failure, node not exist\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	err = n.Propose(context.TODO(), json_data)
	if err != nil {
		fmt.Printf("err:%v\n", err)
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func snopshotAppendHandler(resp http.ResponseWriter, req *http.Request) {
	//data 从哪里获得
	data := []byte("somedata")
	cs := &raftpb.ConfState{Nodes: []uint64{1, 2, 3}}

	//URL get id
	leadId := nodes[0].Status().Lead

	nodeLead := getNode(leadId)
	if nodeLead == nil {
		fmt.Printf("snapshotAppendHandler getNode failed\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	fmt.Printf("---------->>>>> snapshotAppendHandler leadId:%d Applied:%d\n", leadId, nodeLead.Status().Applied)
	snap, err := nodeLead.storage.CreateSnapshot(nodeLead.Status().Applied, cs, data)
	if err != nil {
		fmt.Printf("CreateSnapshot err !!!!!!!!!!!!!!!\n")
		util.Response(nil, http.StatusBadRequest, resp)
		return
	}

	//save snap
	fmt.Printf("snopshotAppend snap:%v\n", snap)

	//Applied
	nodeLead.storage.Compact(nodeLead.Status().Applied)

	resp.Header().Set("Content-Type", "Application/json")
	util.Response(nil, http.StatusOK, resp)
}

func dumpAllNodeHandler(resp http.ResponseWriter, req *http.Request) {
	for _, node := range nodes {
		fmt.Printf("------------id: %v begin ------------- \n", node.id)
		//node.storage.Dump()
		fmt.Printf("state: %v\n", node.state.String())
		fmt.Printf("Status(): %v\n", node.Status().String())
		fmt.Printf("------------id: %v end ------------- \n", node.id)
	}
}
